/*
*         Copyright (c), NXP Semiconductors Bangalore / India
*
*                     (C)NXP Semiconductors
*       All rights are reserved. Reproduction in whole or in part is
*      prohibited without the written consent of the copyright owner.
*  NXP reserves the right to make changes without notice at any time.
* NXP makes no warranty, expressed, implied or statutory, including but
* not limited to any implied warranty of merchantability or fitness for any
*particular purpose, or that the use will not infringe any third party patent,
* copyright or trademark. NXP must not be liable for any loss or damage
*                          arising from its use.
*/

#include <ph_Status.h>
#include <ph_RefDefs.h>
#include <ph_TypeDefs.h>
#include <string.h>
#include <phTools.h>

#ifdef NXPBUILD__PHAL_MFDFEVX_SAM_X

#include "../phalMfdfEVx_Int.h"
#include "phalMfdfEVx_Sam_X.h"
#include "phalMfdfEVx_Sam_X_Int.h"

phStatus_t phalMfdfEVx_Sam_X_Int_ValidateResponse(void * pDataParams, uint16_t wStatus, uint8_t * pPiccRetCode)
{
	uint8_t bISOMode = 0;

	/* Set ISOMode. */
	bISOMode = (uint8_t) (PHAL_MFDFEVX_RESOLVE_DATAPARAMS(pDataParams)->bWrappedMode ? 1 : 0);

	/* Evaluate the response. */
	if ((wStatus == PH_ERR_SUCCESS) || 
		((wStatus & PH_ERR_MASK) == PHHAL_HW_SAMAV3_ERR_MIFARE_GEN) ||
		((wStatus & PH_ERR_MASK) == PHHAL_HW_SAMAV3_ERR_DESFIRE_GEN))
	{
		if(wStatus != PH_ERR_SUCCESS)
		{
			/* Validate if proper wrapped PICC status is available. First byte should be 91. */
			if(bISOMode)
			{
				if(pPiccRetCode[0] != 0x91)
					return PH_ADD_COMPCODE(PH_ERR_PROTOCOL_ERROR, PH_COMP_AL_MFDFEVX);
			}

			/* Validate the PICC Status. */
			PH_CHECK_SUCCESS_FCT(wStatus, phalMfdfEVx_Int_ComputeErrorResponse(pDataParams, pPiccRetCode[bISOMode ? 1 : 0]));
		}
	}
	else
	{
		if((wStatus & PH_ERR_MASK) == PH_ERR_SUCCESS_CHAINING)
		{
			wStatus = PH_ADD_COMPCODE(PH_ERR_SUCCESS_CHAINING, PH_COMP_AL_MFDFEVX);
		}

		PH_CHECK_SUCCESS(wStatus);
	}

	return PH_ADD_COMPCODE(PH_ERR_SUCCESS, PH_COMP_AL_MFDFEVX);
}

phStatus_t phalMfdfEVx_Sam_X_Int_Iso7816Wrap(void * pDataParams, uint8_t bFirstFrame, uint8_t bLengthPresent, uint8_t bLengthLen, uint8_t bLePresent,
	uint32_t dwTotDataLen, uint8_t * pPlainData, uint16_t * pDataLen)
{
	uint8_t		PH_MEMLOC_REM bCmdCode = 0;
	uint8_t		PH_MEMLOC_REM bLC = 0;
	uint8_t		PH_MEMLOC_REM bOffset = 0;
	uint8_t		PH_MEMLOC_REM bOffset1 = 0;
	uint8_t		PH_MEMLOC_REM bIsIsoChainnedCmd_Read = PH_OFF;
	uint8_t		PH_MEMLOC_REM bIsIsoChainnedCmd_Write = PH_OFF;

	/* Extract the command code. */
	if(bFirstFrame)
	{
		bCmdCode = pPlainData[bLengthPresent ? bLengthLen :0];
	}

	/* Set the flag for data operation commands. */
	bIsIsoChainnedCmd_Read = (uint8_t) (((bCmdCode == PHAL_MFDFEVX_CMD_READ_DATA_ISO) || (bCmdCode == PHAL_MFDFEVX_CMD_READ_RECORDS_ISO)) ? PH_ON : PH_OFF);
	bIsIsoChainnedCmd_Write = (uint8_t) (((bCmdCode == PHAL_MFDFEVX_CMD_WRITE_DATA_ISO) || (bCmdCode == PHAL_MFDFEVX_CMD_WRITE_RECORD_ISO) ||
		(bCmdCode == PHAL_MFDFEVX_CMD_UPDATE_RECORD_ISO)) ? PH_ON : PH_OFF);

	/* Wrap the command if needed. */
	if(PHAL_MFDFEVX_RESOLVE_DATAPARAMS(pDataParams)->bWrappedMode && (*pDataLen != 0))
	{
		/* Set the LC value */
		bLC = (uint8_t) (bFirstFrame ? (*pDataLen - 1 /* Removing the command code. */) : *pDataLen);

		/* Subtract LC by 3 if Length is present. */
		bLC = (uint8_t) (bLengthPresent ? ( bLC - bLengthLen)  : bLC);

		/* Compute the offset. */
		bOffset = (uint8_t) ((bFirstFrame && bLengthPresent) ? (bLengthLen + 1) : 1);

		/* Compute the offset to be used for moving the current data. */
		bOffset1 = (uint8_t) (bLengthPresent ? (bLengthLen + 5) : 5);

		/* Copy the data to the Pointer. */
		memmove(&pPlainData[bOffset1], &pPlainData[bOffset], bLC);	/* PRQA S 3200 */

		/* Reset the length buffer. */
		*pDataLen = bLengthPresent ? bLengthLen : 0;

		/* Frame the initial ISO7816 header. */
		pPlainData[(*pDataLen)++] = PHAL_MFDFEVX_WRAPPEDAPDU_CLA;
		pPlainData[(*pDataLen)++] = bCmdCode;
		pPlainData[(*pDataLen)++] = PHAL_MFDFEVX_WRAPPEDAPDU_P1;
		pPlainData[(*pDataLen)++] = PHAL_MFDFEVX_WRAPPEDAPDU_P2;

		/* Add LC if there is data. */
		if(bLC)
		{
			pPlainData[(*pDataLen)++] = (uint8_t) (bIsIsoChainnedCmd_Write ? dwTotDataLen : bLC);

			/* Update Frame length based on LC. */
			*pDataLen = *pDataLen + bLC;
		}

		if(bLePresent)
		{
			pPlainData[(*pDataLen)++] = PHAL_MFDFEVX_WRAPPEDAPDU_LE;
		}
	}

	return PH_ADD_COMPCODE(PH_ERR_SUCCESS, PH_COMP_AL_MFDFEVX);
}

phStatus_t phalMfdfEVx_Sam_X_Int_GetKeyInfo(void * pDataParams, uint8_t bKeyNo, uint16_t * pKeyType, uint16_t * pSET, uint16_t * pExtSET)
{
	phStatus_t	PH_MEMLOC_REM wStatus = 0;
	uint16_t	PH_MEMLOC_REM wHostMode = 0;
	uint8_t		PH_MEMLOC_REM bIsAV3 = 0;
	uint8_t		PH_MEMLOC_REM bIsRamKey = 0;
	uint8_t		PH_MEMLOC_REM bMode = 0;
	uint8_t		PH_MEMLOC_REM bOffset = 0;
	uint8_t		PH_MEMLOC_REM aKeyEntry[16];
	uint8_t		PH_MEMLOC_REM bKeyEntryLen = 0;

	/* Get the HostMode. */
	PH_CHECK_SUCCESS_FCT(wStatus, phhalHw_GetConfig(
		PHAL_MFDFEVX_RESOLVE_HAL_DATAPARAMS(pDataParams), 
		PHHAL_HW_SAMAV3_CONFIG_HOSTMODE,
		&wHostMode));

	/* Set the flag. */
	bIsAV3 = (uint8_t) ((wHostMode == PHHAL_HW_SAMAV3_HC_AV3_MODE) ? PH_ON : PH_OFF);
	bIsRamKey = (uint8_t) ((bKeyNo >= 0xE0) && (bKeyNo <= 0xE3)) ? PH_ON : PH_OFF;

	/* Set the Mode. */
	bMode = PHHAL_HW_SAMAV3_CMD_SAM_GET_KEY_ENTRY_KEY_ENTRY_NEW;
	bMode = (uint8_t) (!bIsAV3 ? PHHAL_HW_SAMAV3_CMD_SAM_GET_KEY_ENTRY_KEY_ENTRY_SAM_AV2 : bMode);
	bMode = (uint8_t) (bIsRamKey ? PHHAL_HW_SAMAV3_CMD_SAM_GET_KEY_ENTRY_RAM_KEY : bMode);

	/* Get the KeyInformation. */
	PH_CHECK_SUCCESS_FCT(wStatus, phhalHw_SamAV3_Cmd_SAM_GetKeyEntry(
		PHAL_MFDFEVX_RESOLVE_HAL_DATAPARAMS(pDataParams),
        bKeyNo,
		bMode,
		aKeyEntry,
		&bKeyEntryLen));

	/* Extract SET configuration. */
	bOffset = (uint8_t) (bIsAV3 ? (bKeyEntryLen - 6) : (bKeyEntryLen - 3));
	*pSET = (uint16_t) ((aKeyEntry[bOffset + 1] << 8) | aKeyEntry[bOffset]);

	/* Extract ExtSET configuration. */
	bOffset = (uint8_t) (bIsAV3 ? (bKeyEntryLen - 4) : (bKeyEntryLen - 1));
	*pExtSET = (uint16_t) ((bIsAV3 ? (aKeyEntry[bOffset + 1] << 8) : 0) | aKeyEntry[bOffset]);

	/* Extract the KeyType. */
	*pKeyType = (uint16_t) (*pSET & 0x0078);

	return PH_ADD_COMPCODE(PH_ERR_SUCCESS, PH_COMP_AL_MFDFEVX);
}

phStatus_t phalMfdfEVx_Sam_X_Int_ComputeTMACSessionVectors(void * pDataParams, uint8_t bOption, uint16_t wSrcKeyNo, uint16_t wSrcKeyVer, uint16_t wDstKeyNo, 
	uint8_t * pTMC, uint8_t * pUid, uint8_t bUidLen)
{
	phStatus_t	PH_MEMLOC_REM wStatus = 0;
	uint8_t		PH_MEMLOC_REM aSV[16];
	uint8_t		PH_MEMLOC_REM aIV[16];
	uint8_t		PH_MEMLOC_REM bSvLen = 0;
	uint32_t	PH_MEMLOC_REM dwTMC = 0;

	/* Convert TMC to Uint32. */
	dwTMC = (uint32_t) (pTMC[0] | (pTMC[1] << 8) | (pTMC[2] << 16) | (pTMC[3] << 24));

    /* If TMC is 0xFFFFFFFF, then return error */
    if (dwTMC == 0xFFFFFFFF)
    {
        return PH_ADD_COMPCODE(PH_ERR_PARAMETER_OVERFLOW, PH_COMP_AL_MFPEVX);
    }

	/* Increment the TMC by 1. */
	dwTMC++;

	/* Clear the session vector SV. */
	memset(aSV, 0, 16);		/* PRQA S 3200 */
	memset(aIV, 0, 16);		/* PRQA S 3200 */

	/* Compute the session vector. */
	aSV[bSvLen++] = (uint8_t) ((bOption == PHAL_MFDFEVX_SAM_X_SESSION_TMAC_ENC) ? 0xA5 : 0x5A);
	aSV[bSvLen++] = 0x00;
	aSV[bSvLen++] = 0x01;
	aSV[bSvLen++] = 0x00;
	aSV[bSvLen++] = 0x80;

	/* Append the TMC information. */
	aSV[bSvLen++] = (uint8_t) (dwTMC & 0xFF);
	aSV[bSvLen++] = (uint8_t) ((dwTMC & 0xFF00) >> 8);
	aSV[bSvLen++] = (uint8_t) ((dwTMC & 0xFF0000) >> 16);
	aSV[bSvLen++] = (uint8_t) ((dwTMC & 0xFF000000) >> 24);

	/* Append the UID infomration. */
	memcpy(&aSV[bSvLen], pUid, bUidLen);	/* PRQA S 3200 */
	bSvLen = 16;

	/* Load zero IV. */
	PH_CHECK_SUCCESS_FCT(wStatus, phhalHw_SamAV3_Cmd_SAM_LoadInitVector(
		PHAL_MFDFEVX_RESOLVE_HAL_DATAPARAMS(pDataParams),
		PHHAL_HW_SAMAV3_CMD_SAM_LOAD_IV_MODE_SET_IV,
		aIV,
		16));

	/* Exchange the session vector information to SAM. */
	PH_CHECK_SUCCESS_FCT(wStatus, phhalHw_SamAV3_Cmd_SAM_DeriveKey(
		PHAL_MFDFEVX_RESOLVE_HAL_DATAPARAMS(pDataParams),
		(uint8_t) wSrcKeyNo,
		(uint8_t) wSrcKeyVer,
		(uint8_t) wDstKeyNo,
		aSV,
		16));

	return PH_ADD_COMPCODE(PH_ERR_SUCCESS, PH_COMP_AL_MFDFEVX);
}

phStatus_t phalMfdfEVx_Sam_X_Int_ComputeSDMSessionVectors(void * pDataParams, uint8_t bOption, uint8_t bSdmOption, uint16_t wSrcKeyNo, uint16_t wSrcKeyVer, 
	uint16_t wDstKeyNo, uint8_t * pUid, uint8_t bUidLen, uint8_t * pSDMReadCtr)
{
	phStatus_t	PH_MEMLOC_REM wStatus = 0;
	uint8_t		PH_MEMLOC_REM aSV[32];
	uint8_t		PH_MEMLOC_REM bSvLen = 0;
	uint32_t	PH_MEMLOC_REM dwSDMReadCtr = 0;

	/* Validate the Counter value. */
	if(pSDMReadCtr != NULL)
	{
		dwSDMReadCtr = (uint32_t) (pSDMReadCtr[0] | (pSDMReadCtr[1] << 8) | (pSDMReadCtr[2] << 16) | (pSDMReadCtr[3] << 24));
		if (dwSDMReadCtr == 0xFFFFFF)
		{
			return PH_ADD_COMPCODE(PH_ERR_PARAMETER_OVERFLOW, PH_COMP_AL_MFDFEVX);
		}
	}

	/* Clear the session vector SV. */
	memset(aSV, 0, sizeof(aSV));	/* PRQA S 3200 */

	/* Frame the default values in session vector. */
	aSV[bSvLen++] = (uint8_t) ((bOption == PHAL_MFDFEVX_SESSION_ENC) ? 0xC3 : 0x3C);
	aSV[bSvLen++] = (uint8_t) ((bOption == PHAL_MFDFEVX_SESSION_ENC) ? 0x3C : 0xC3);
	aSV[bSvLen++] = 0x00;
	aSV[bSvLen++] = 0x01;
	aSV[bSvLen++] = 0x00;
	aSV[bSvLen++] = 0x80;

	/* Append the UID */
	if (bSdmOption & PHAL_MFDFEVX_VCUID_PRESENT)
	{
		if(pUid != NULL)
		{
			memcpy(&aSV[bSvLen], pUid, bUidLen);	/* PRQA S 3200 */
			bSvLen += bUidLen;
		}
		else
			return PH_ADD_COMPCODE(PH_ERR_INVALID_PARAMETER, PH_COMP_AL_MFDFEVX);
	}

	/* Append the SDM ReadCtr information. */
	if (bSdmOption & PHAL_MFDFEVX_RDCTR_PRESENT)
	{
		if(dwSDMReadCtr != NULL)
		{
			aSV[bSvLen++] = (uint8_t) (dwSDMReadCtr & 0xFF);
			aSV[bSvLen++] = (uint8_t) ((dwSDMReadCtr & 0xFF00) >> 8);
			aSV[bSvLen++] = (uint8_t) ((dwSDMReadCtr & 0xFF0000) >> 16);
		}		
	}

	/* Update the SV length */
	if((bSdmOption & PHAL_MFDFEVX_RDCTR_PRESENT) && (bSvLen > 16))
		bSvLen = 32;
	else
		bSvLen = 16;

	/* Exchange the session vector information to SAM. */
	PH_CHECK_SUCCESS_FCT(wStatus, phhalHw_SamAV3_Cmd_SAM_DeriveKey(
		PHAL_MFDFEVX_RESOLVE_HAL_DATAPARAMS(pDataParams),
		(uint8_t) wSrcKeyNo,
		(uint8_t) wSrcKeyVer,
		(uint8_t) wDstKeyNo,
		aSV,
		bSvLen));

	return PH_ADD_COMPCODE(PH_ERR_SUCCESS, PH_COMP_AL_MFDFEVX);
}

phStatus_t phalMfdfEVx_Sam_X_Int_LoadSDMInitVector(void * pDataParams, uint8_t * pSDMReadCtr)
{
	phStatus_t	PH_MEMLOC_REM wStatus = 0;
	uint8_t		PH_MEMLOC_REM bOption = 0;
	uint8_t		PH_MEMLOC_REM aIV[16];
	uint8_t *	PH_MEMLOC_REM pIV = NULL;
	uint16_t	PH_MEMLOC_REM wIvLen = 0;

	/* Clear the Initialization Vector. */
	memset(aIV, 0, 16);		/* PRQA S 3200 */

	/* IV computation is E(KSesSDMFileReadENC; SDMReadCtr || 0x00000000000000000000000000) */
	memcpy(&aIV[wIvLen], pSDMReadCtr, 3);	/* PRQA S 3200 */
		
	/* Set the IV length to 16. */
	wIvLen = 16;

	/* Encrypt the IV. */
	PH_CHECK_SUCCESS_FCT(wStatus, phhalHw_SamAV3_Cmd_SAM_EncipherOfflineData(
		PHAL_MFDFEVX_RESOLVE_HAL_DATAPARAMS(pDataParams),
		PH_EXCHANGE_DEFAULT,
		aIV,
		(uint8_t) wIvLen,
		&pIV,
		&wIvLen));

	/* Copy the enciphered data to local buffer. */
	memcpy(aIV, pIV, wIvLen);		/* PRQA S 3200 */		
	pIV = NULL;

	/* Set the Option. */
	bOption = PHHAL_HW_SAMAV3_CMD_SAM_LOAD_IV_MODE_SET_IV;

	/* Load the IV. */
	PH_CHECK_SUCCESS_FCT(wStatus, phhalHw_SamAV3_Cmd_SAM_LoadInitVector(
			PHAL_MFDFEVX_RESOLVE_HAL_DATAPARAMS(pDataParams),
			bOption,
			aIV,
			(uint8_t) wIvLen));

	return PH_ADD_COMPCODE(PH_ERR_SUCCESS, PH_COMP_AL_MFDFEVX);
}

#endif /* NXPBUILD__PHAL_MFDFEVX_SAM_X */
